//-----------------------------------------------------------------------------
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <string>
#include <iostream>
#include <cstdint>

#ifdef _MSC_VER
#include <winsock.h>
#pragma comment(lib,"ws2_32.lib")
#endif 

#ifdef __unix__
#include <netinet/in.h>
#endif
//-----------------------------------------------------------------------------

/**
 * @brief	用于IP地址转换的辅助类
 *			可以将IP地址在 字符串形式 和 整形形式 之间互相转换
 *			IP4地址整形表示时为32位,以uint32_t存储
 *			IP6地址整形表示时为128位, 以uint8_t [16] 表示
 */
class IP_Convert {

	union ipv6 {
		uint16_t s[8];
		uint8_t data[16];
	};

	union ipv4 {
		uint32_t data;
		uint8_t s[4];
	};

	//-------------------------------------------------------------------------
	/**
	 * @brief	移除字符串中空格
	 */
	static char* strim(char* str)
	{
		int len = strlen(str);
		int i = 0, j = 0;
		for (; i < len; i++) {
			if (!isspace(str[i])) {
				str[j] = str[i];
				j++;
			}
		}

		str[j] = '\0';

		return str;
	}

	//-------------------------------------------------------------------------
	/**
	 * @brief	计算字符串中，给定字符的数量
	 */
	static int count_char(const char* string, char character)
	{
		int i;
		int str_length;
		int count = 0;

		str_length = strlen(string);
		for (i = 0; i < str_length; i++) {
			if (string[i] == character) {
				count++;
			}
		}

		return count;
	}

	//-------------------------------------------------------------------------
	// 表示IP6地址中每个域的字符串表示形式
	enum E_IP6_FIELD_TYPE 
	{
		E_ERROR,	// 错误
		E_EMPTY,	// :: 表示的省略写法
		E_HEX,		// FFFF 形式的16进制表示
		E_IPV4		// 192.168.1.1 形式的写法
	};

	//-------------------------------------------------------------------------
	/**
	 * @brief	判断ipv6地址域类型, 通过长度判断
	 *				16进制数字域： 1-4
	 *				"::"域：0
	 *				ipv4地址域： 7-15
	 */
	static enum E_IP6_FIELD_TYPE get_ip6_filed_type(const char* field_str)
	{
		int length = strlen(field_str);

		if (0 == length)
			return E_EMPTY;

		if (length <= 4) {
			for (int i = 0; i < length; i++)
				if (!isxdigit(field_str[i]))
					return E_ERROR;

			return E_HEX;
		} else if ((length >= 7) && (length <= 15)) {
			//确保是有效的ipv4地址
			if (str_to_ip4(field_str, nullptr)) {
				return E_IPV4;
			} else {
				return E_ERROR;
			}
		} else {
			return E_ERROR;
		}
	}

public:
	//-------------------------------------------------------------------------
	/**
	 * @brief	32位整形表示的IP地址转为字符串型
	 *			不会失败
	 */
	static std::string ip4_to_str(uint32_t ip_value)
	{
		union ipv4 temp;
		temp.data = ip_value;
		std::ostringstream oss;
		oss << (int)temp.s[0] << "." << (int)temp.s[1] << "." << (int)temp.s[2] << "." << (int)temp.s[3];
		return oss.str();
	}

	//-------------------------------------------------------------------------
	/**
	 * @brief	ipv4地址转整型
	 * @param	ipv4_addr	字符ip地址,"192.168.1.254"
	 * @param	[OUT] ipv4_num	返回数字ip
	 * @return	是否成功
	 */
	static bool str_to_ip4(const char* ipv4_str, uint32_t* ipv4_num)
	{
		int len = strlen(ipv4_str);
		if ((len > 15) || (len < 7)) /*ip地址长度错误*/
			return false;

		uint32_t result = 0;

		char buff[4] = { 0 };
		uint32_t temp = 0;

		int j = 0, k = 3;
		for (int i = 0; i <= len; i++) {
			if (ipv4_str[i] == '\0' || ipv4_str[i] == '.') {
				if ((j == 0) || (j > 3))		// 长度超出
					return false;
				buff[j] = '\0';					//结束符
				temp = atoi(buff);
				if (temp > 255)
					return false;				//超出范围
				result |= temp << (k * 8);
				if (k == 0)
					break;						//强制结束，ip地址点分超出时，只取前面
				k--;
				j = 0;
				buff[0] = '\0';					//清空缓存
			} else {
				buff[j] = ipv4_str[i];
				j++;
			}
		}

		if (nullptr != ipv4_num)
			*ipv4_num = result;
		return true;
	}

	//-------------------------------------------------------------------------
	/*
	/**
	 * @brief	ipv6 无符号整型数组转化为字符串
	 *			ipv6地址128位，数组ip维数默认为4
	 *			输出格式为: A:B:C:D:E:F:G:H
	 * @param	ipv6_addr IP6地址,以字节形式存储的整形表示（网络字节序）
	 * @return	string 字符串形式表示的IP6地址
	 **/
	static std::string ip6_to_str(void* ipv6_addr)
	{
		union ipv6 temp;
		memcpy(temp.data, ipv6_addr, sizeof(temp.data));
		std::ostringstream oss;
		for (int i = 0; i < sizeof(temp.s) / sizeof(unsigned short); i++)
		{
			if (i != 0)
				oss << ":";
			oss << std::hex << ntohs(temp.s[i]);
		}
		return oss.str();
	}

	//-------------------------------------------------------------------------
	/**
	 * @brief	解析ipv6地址字符串，转换为无符号整形,存入无符号整形的一维数组 
	 *			ipv6地址 128位，prefix length:                 						  
	 *                         - 64 for EUI-64 addresses                    
	 *                         - 128 for non-EUI-64 addresses                 
	 * @param	addr_str		ipv6地址字符串，地址位数，默认为128位     
	 * @param	[OUT] presult	调用者分配的用于存储转换结果的空间，16字节
	 * @return	返回解析成功或失败;  指向4个无符号整形的一维数组的指针             
	 */
	static bool str_to_ip6(const char* addr_str, uint8_t * presult, int length = 128)
	{
		char addr_str_copy[256] = { 0 };
		char* ith_field = nullptr; // 指向地址当前域
		char* next_field = nullptr;
		uint16_t addr_field_arr[8] = { 0 };	// 8个域
		int  double_colon = -1; // 字符串地址中"::"的位置
		uint32_t ipv4_address = 0; // ipv6地址中的ipv4部分
		unsigned int msw = 0, lsw = 0;
		int error = 0;
		int max_fields = length >> 4; // length / 16 = 8

		//复制一份，以便操作
		strcpy_s(addr_str_copy, addr_str);

		// 移除字符串中的空格字符
		strim(addr_str_copy);

		/* IPv6地址可能几种格式：                                          */
		/* 1) 2006:DB8:2A0:2F3B:34:E35:45:1   用16进制表示每个域的值(16位) */
		/* 2) 2006:DB8::E34:1 , "::" 代表0，且只能出现一次       */
		/* 3) 2002:9D36:1:2:0:5EFE:192.168.12.9 带有ipv4地址     */

		// 计算字符串中冒号，字符串中地址域数比冒号多一个
		int num_fields = count_char(addr_str_copy, ':') + 1;

		// 冒号分隔的域最大数量为length/16 + 2
		// 如  ::0:0:0:0:0:0:0:0 (前两个域会被忽略)
		if (num_fields > (max_fields + 2))
			return false;

		// 初始化
		ith_field = addr_str_copy;
		int addr_index = 0;
		for (int i = 0; i < num_fields; i++) {
			
			if (nullptr == ith_field) break;

			// 获得下一个域的指针
			next_field = strchr(ith_field, ':');

			/* 若当前是最后一个域, next_field 是 NULL                       */
			/* 否则，替换':'为'\0', 字符串可以结束，从而ith_field指向当前域   */
			/* next_field指向下一个域头部                                    */
			if (nullptr != next_field) {
				*next_field = '\0';
				++next_field;
			}

			// 发现这个域的类型
			enum E_IP6_FIELD_TYPE type = get_ip6_filed_type(ith_field);
			switch (type) {
			case E_HEX:
				// 域类型为16进制表示
				if (addr_index >= max_fields)
					return false;

				// 字符串转换为16进制
				addr_field_arr[addr_index] = (uint16_t)strtoul(ith_field, NULL, 16);
				++addr_index;
				break;
			case E_EMPTY:
				// 域类型为 "::"
				// 若出现在字符串的开头或结尾，忽略
				if ((0 == i) || (i == num_fields - 1))
					break;

				// 若出现大于一次，错误
				if (double_colon != -1)
					return false;

				double_colon = addr_index;
				break;
			case E_IPV4:
				// 域类型为ipv4地址
				// 确保在地址中还有两个未设置的域
				if (addr_index >= 7)
					return false;

				// ipv4地址解析
				if (!str_to_ip4(ith_field, &ipv4_address))
					return false;

				// 存储高16位
				addr_field_arr[addr_index] = (unsigned short)(ipv4_address >> 16);
				// 存储低16位
				addr_field_arr[addr_index + 1] = (unsigned short)(ipv4_address & 0x0000ffff);
				addr_index += 2;

				break;
			default:
				return false;
			}
			ith_field = next_field;
		}

		// 计算的域不是8，并且没有"::",错误
		if ((addr_index != max_fields) && (-1 == double_colon))
			return false;

		if ((addr_index != max_fields) && (-1 != double_colon)) {
			// 先将中间的空位移动出来, 再设置相应"::"对应addr_field_arr中位置为0
			int space = (max_fields - addr_index);
			int tail = addr_index - double_colon; // 双冒号后面域的个数
			memmove(addr_field_arr + double_colon + space, addr_field_arr + double_colon, tail * 2);
			memset(addr_field_arr + double_colon, 0, space * 2);
		}

		for (int i = 0; i < 8; i++) {
			presult[2 * i] = (uint8_t)(addr_field_arr[i] >> 8);
			presult[2 * i + 1] = (uint8_t)(addr_field_arr[i] & 0x00FF);
		}

		return true;
	}
};

//-----------------------------------------------------------------------------

// 以下是测试代码
static unsigned char ipv6_test[][16] = {
	{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF},
	{0xF, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0}
};

static const char* ipv6_str_test[] = {
	"1:2:3:4:5:6:7:8",
	"1:2:3:4:5:6:7:8::",
	"::1:2:3:4:5:6:7:8",
	"1:2:3:4:5:6:192.168.1.100",
	"1:2::5:6:7:8",
	"1::3:4:5:6:7:8",
	"1::4:5:6:7:8",
	"1::8"
};

//-----------------------------------------------------------------------------

int main()
{
	for (int i = 0; i < sizeof(ipv6_test) / 16; i++) {
		std::cout << IP_Convert::ip6_to_str(ipv6_test[i]) << std::endl;
	}

	uint8_t ip6[16];

	for (int i = 0; i < sizeof(ipv6_str_test) / sizeof(const char *); i++) {
		if (IP_Convert::str_to_ip6(ipv6_str_test[i], ip6)) {
			std::cout << "Origin: " << ipv6_str_test[i] 
				<< " - Convert: " << IP_Convert::ip6_to_str(ip6) << std::endl;
		} else {
			std::cout << "Error: " << ipv6_str_test[i] << std::endl;
		}
		
	}
	return 0;
}

//-----------------------------------------------------------------------------